package com.example.warehouse.sys.service.impl;

import com.alibaba.fastjson.JSON;
import com.example.warehouse.common.ex.ServiceException;
import com.example.warehouse.common.pojo.po.UserLoginInfoPO;
import com.example.warehouse.common.pojo.vo.PageData;
import com.example.warehouse.common.web.ServiceCode;
import com.example.warehouse.sys.dao.cache.IUserCacheRepository;
import com.example.warehouse.sys.dao.persist.respository.ISysLogInFoRepository;
import com.example.warehouse.sys.dao.persist.respository.IUserRepository;
import com.example.warehouse.sys.pojo.entity.SysLogInfo;
import com.example.warehouse.sys.pojo.entity.SysUser;
import com.example.warehouse.sys.pojo.param.AddNewUserParam;
import com.example.warehouse.sys.pojo.param.SysUserStandardParam;
import com.example.warehouse.sys.pojo.param.UserLoginInfoParam;
import com.example.warehouse.sys.pojo.param.UserUpdateParam;
import com.example.warehouse.sys.pojo.vo.*;
import com.example.warehouse.sys.security.CustomUserDetails;
import com.example.warehouse.sys.service.IUserService;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.*;

@Slf4j
@Service
public class UserServiceImpl implements IUserService {
    @Value("${warehouse.jwt.duration-in-minute}")
    private long durationInMinute;
    @Value("${warehouse.jwt.secret-key}")
    private String secretKey;
    @Value("${warehouse.dao.default-query-page-size}")
    private Integer defaultQueryPageSize;

    @Autowired
    private IUserRepository iUserRepository;
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private IUserCacheRepository userCacheRepository;
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private ISysLogInFoRepository sysLogInFoRepository;

    @Override
    public UserLoginResultVO login(UserLoginInfoParam userLoginInfoParam, String remoteAddr, String userAgent) {
        log.debug("开始处理【用户登录】的业务，参数：{}", userLoginInfoParam);
        Authentication authentication = new UsernamePasswordAuthenticationToken(
                userLoginInfoParam.getUsername(), userLoginInfoParam.getPassword());
        log.debug("准备调用AuthenticationManager的认证方法，判断此用户名、密码是否可以成功登录……");
        Authentication authenticateResult
                = authenticationManager.authenticate(authentication);
        log.debug("验证用户登录成功，返回的认证结果：{}", authenticateResult);

        Object principal = authenticateResult.getPrincipal();
        log.debug("从认证结果中获取当事人：{}", principal);

        CustomUserDetails userDetails = (CustomUserDetails) principal;

        Long id = userDetails.getId();
        log.debug("从认证结果中的当事人中获取ID：{}", id);

        String name = userDetails.getUsername();
        log.debug("从认证结果中的当事人中获取用户名：{}", name);

        String avatar = userDetails.getAvatar();
        log.debug("从认证结果中的当事人中获取头像：{}", avatar);

        Collection<? extends GrantedAuthority> authorities = userDetails.getAuthorities();
        log.debug("从认证结果中的当事人中获取权限列表：{}", authorities);

        String authoritiesJsonString = JSON.toJSONString(authorities);
        log.debug("将权限列表对象转换为JSON格式的字符串：{}", authoritiesJsonString);

        Date date = new Date(System.currentTimeMillis() + 1L * 60 * 1000 * durationInMinute);
        //                                                 ↑ 注意加L，避免int溢出为负数
        Map<String, Object> claims = new HashMap<>();
        claims.put("id", id);
        claims.put("name", name);
        // 生成JWT时，不再存入权限列表
        // claims.put("authoritiesJsonString", authoritiesJsonString);
        String jwt = Jwts.builder()
                .setHeaderParam("alg", "HS256")
                .setHeaderParam("typ", "JWT")
                .setClaims(claims)
                .setExpiration(date)
                .signWith(SignatureAlgorithm.HS256, secretKey)
                .compact();

        // 生成JWT之后，需要将权限列表存入到Redis中
        UserLoginInfoPO userLoginInfoPO = new UserLoginInfoPO();
        userLoginInfoPO.setUserAgent(userAgent);
        userLoginInfoPO.setIp(remoteAddr);
        userLoginInfoPO.setAuthoritiesJsonString(authoritiesJsonString);
        userCacheRepository.saveLoginInfo(jwt,userLoginInfoPO);

        // 将用户状态存入到Redis中
        userCacheRepository.saveEnableByUserId(id, 1);

        /**记录登录入日志*/
        SysLogInfo sysLogInfo = new SysLogInfo();
        sysLogInfo.setNickname(selectNickenameById(id));
        sysLogInfo.setLoginIp(remoteAddr);
        sysLogInFoRepository.insert(sysLogInfo);


        UserLoginResultVO userLoginResultVO = new UserLoginResultVO()
                .setId(id)
                .setName(name)
                .setNickname(selectNickenameById(id))
                .setAvatar(avatar)
                .setToken(jwt);
        return userLoginResultVO;
    }

    @Override
    public void addNew(AddNewUserParam addNewUserParam) {
        log.debug("开始执行 [创建用户] 业务");
        String name = addNewUserParam.getName();
        int count = iUserRepository.countByName(name);
        if (count>0){
            String message = "注册失败,该账号已存在!";
            log.debug(message);
            throw new ServiceException(ServiceCode.ERROR_CONFLICT,message);
        }

        SysUser user = new SysUser();
        BeanUtils.copyProperties(addNewUserParam,user);
        String rawPassword = user.getPassword();
        String encodedPassword = passwordEncoder.encode(rawPassword);
        user.setPassword(encodedPassword);
        iUserRepository.insert(user);
    }

    @Override
    public void deleteById(Long id) {
        log.debug("开始执行 [根据id删除用户] 业务");
        int count = iUserRepository.countById(id);
        if (count==0){
            String message = "删除失败,该账户不存在!";
            log.debug(message);
            throw new ServiceException(ServiceCode.ERROR_NOT_FOUND,message);
        }
        log.debug("开始执行 [删除用户角色信息] 业务");
        Long i = iUserRepository.selectByRoleId(id);
        iUserRepository.deleteByRoleId(i);

        iUserRepository.deleteById(id);
    }


    @Override
    public void updateByPassword(Long id) {
        log.debug("开始执行 [重置密码] 业务, 默认密码为: 123456");
        SysUser user = new SysUser();
        user.setId(id);
        String encodedPassword = passwordEncoder.encode("123456");
        user.setPassword(encodedPassword);
        iUserRepository.updateById(user);
    }

    @Override
    public void updateBySysUser(SysUserStandardParam sysUserStandardParam) {
        log.debug("开始执行 [修改用户参数] 业务");
        if (sysUserStandardParam.getPassword()==null){
            String message = "修改失败, 请输入密码!";
            log.debug(message);
            throw new ServiceException(ServiceCode.ERROR_BAD_REQUEST,message);
        }

        if (sysUserStandardParam.getNickname()==null){
            String message = "修改失败, 请输入登录名!";
            log.debug(message);
            throw new ServiceException(ServiceCode.ERROR_BAD_REQUEST,message);
        }

        SysUser user = new SysUser();
        BeanUtils.copyProperties(sysUserStandardParam,user);
        iUserRepository.updateById(user);
    }

    @Override
    public void updateByPersonal(Long id,UserUpdateParam userUpdateParam) {

        log.debug("开始执行 [个人中心修改信息] 业务");
        SysUser user = new SysUser();
        user.setId(id);
        BeanUtils.copyProperties(userUpdateParam,user);
        iUserRepository.updateById(user);


    }

    @Override
    public void setEnable(Long id) {
        updateEnableById(id,1);
    }

    @Override
    public void setDisable(Long id) {
        updateEnableById(id,0);
    }

    @Override
    public List<UserCustomerStandardVO> queryByKeyWord(String deptName, String userName, String address) {
        log.debug("开始处理【模糊查询】的业务， 参数:{},{},{}",deptName,userName,address);
        return iUserRepository.queryByKeyWord(deptName,userName,address);
    }

    @Override
    public PageData<SysUserListItemVO> list(Integer pageNum) {
        log.debug("开始处理【查询用户列表】的业务，页码：{}", pageNum);
        PageData<SysUserListItemVO> pageData = iUserRepository.list(pageNum, defaultQueryPageSize);
        return pageData;
    }

    @Override
    public List<SysUser> selectAll() {
        List<SysUser> list = iUserRepository.selectAll();
        return list;
    }

    @Override
    public String selectNickenameById(Long id) {
        return iUserRepository.selectNicknameById(id);
    }

    @Override
    public UserPersonalVO selectById(Long id) {
        log.debug("开始处理【查询用户信息】的业务，id：{}", id);
        UserPersonalVO userPersonalVO = iUserRepository.getPersonalById(id);
        return userPersonalVO;
    }

    //用户中心用户自行修改密码
    @Override
    public void updatePasswordById(Long id, String password) {
        log.debug("开始处理【用户修改密码】的业务，id：{},password: {}", id,password);
        if (password==null || password.isEmpty()){
            String message = "修改失败, 请输入密码!";
            log.debug(message);
            throw new ServiceException(ServiceCode.ERROR_BAD_REQUEST,message);
        }
        SysUser user = new SysUser();
        user.setId(id);
        String p = passwordEncoder.encode(password);
        user.setPassword(p);
        iUserRepository.updateById(user);
    }

    private void updateEnableById(Long id, Integer enable){
        log.debug("开始处理【{}用户】的业务，ID：{}，目标状态：{}", ENABLE_TEXT[enable], id, enable);
        UserStandardVO standardById = iUserRepository.getStandardById(id);
        if (standardById==null){
            String message = ENABLE_TEXT[enable]+"用户失败,尝试访问的数据不存在!";
            log.debug(message);
            throw new ServiceException(ServiceCode.ERROR_NOT_FOUND,message);
        }

        if (standardById.getEnable().equals(enable)){
            String message = ENABLE_TEXT[enable] + "用户失败，当前用户已经处于"
                    + ENABLE_TEXT[enable] + "状态！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERROR_CONFLICT, message);
        }

        SysUser user = new SysUser();
        user.setId(id);
        user.setEnable(enable);
        log.debug("即将修改数据，参数：{}", user);
        int rows = iUserRepository.updateById(user);
        if (rows != 1) {
            String message = ENABLE_TEXT[enable] + "用户失败，服务器忙，请稍后再次尝试！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERROR_UPDATE, message);
        }

    }

}
